package com.freetymekiyan.algorithms.level.medium;

import java.util.Iterator;

/**
 * 284. Peeking Iterator
 * <p>
 * Given an Iterator class interface with methods: next() and hasNext(), design and implement a PeekingIterator that
 * support the peek() operation -- it essentially peek() at the element that will be returned by the next call to
 * next().
 * <p>
 * Here is an example. Assume that the iterator is initialized to the beginning of the list: [1, 2, 3].
 * <p>
 * Call next() gets you 1, the first element in the list.
 * <p>
 * Now you call peek() and it returns 2, the next element. Calling next() after that still return 2.
 * <p>
 * You call next() the final time and it returns 3, the last element. Calling hasNext() after that should return false.
 * <p>
 * Hint:
 * <p>
 * Think of "looking ahead". You want to cache the next element.
 * Is one variable sufficient? Why or why not?
 * Test your design with call order of peek() before next() vs next() before peek().
 * For a clean implementation, check out Google's guava library source code.
 * <p>
 * Follow up: How would you extend your design to be generic and work with all types, not just integer?
 * <p>
 * Company Tags: Google, Apple, Yahoo
 * Tags: Design
 * Similar Problems: (M) Binary Search Tree Iterator, (M) Flatten 2D Vector, (M) Zigzag Iterator
 */
public class PeekingIterator implements Iterator<Integer> {

  private Integer next;
  private Iterator<Integer> iter;

  /**
   * Peeking iterator is based on normal iterator.
   * Just a wrapper class.
   * Use a variable to cache the next element.
   */
  public PeekingIterator(Iterator<Integer> iterator) {
    // initialize any member here.
    this.iter = iterator;
    if (iterator.hasNext()) {
      next = iterator.next();
    }
  }

  /**
   * Just return the cached element.
   */
  // Returns the next element in the iteration without advancing the iterator.
  public Integer peek() {
    return next;
  }

  /**
   * Return next value.
   * If iterator still has more elements, update next with next.
   * If iterator don't have any more, set next to null.
   */
  // hasNext() and next() should behave the same as in the Iterator interface.
  // Override them if needed.
  @Override
  public Integer next() {
    int res = next;
    next = iter.hasNext() ? iter.next() : null;
    return res;
  }

  /**
   * Just check next.
   */
  @Override
  public boolean hasNext() {
    return next != null;
  }

}
